﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Microsoft.Ink;
using System.Windows.Forms;
using Streambolics.Gui;

namespace Streambolics.Tablet
{
    /// <summary>
    ///     A component that adds ink awareness to a control.
    /// </summary>

    public class InkAdapter : Component
    {
        private InkOverlay _Overlay;
        private Control _Control;
        private Timer _Timer;
        private int _Ticks;

        private static bool _InkEnabled = true;

        #region Recognition Timer

        protected void StartTimer (int ms)
        {
            if (_Timer != null)
            {
                StopTimer ();
            }
            _Timer = new Timer ();
            _Timer.Interval = ms / 4;
            _Ticks = 4;
            _Timer.Tick += new EventHandler (_Timer_Tick);
            _Timer.Start ();
        }

        private void StopTimer ()
        {
            _Timer.Stop ();
            _Timer.Dispose ();
            _Timer = null;
        }

        void _Timer_Tick (object sender, EventArgs e)
        {
            if (_Ticks-- <= 0)
            {
                StopTimer ();
                OnTimerElapsed (e);
            }
        }

        protected void OnTimerElapsed (EventArgs e)
        {
            StartAnalysis ();
        }

        void _Collector_Stroke (object sender, InkCollectorStrokeEventArgs e)
        {
            StartTimer (2000);
        }

        #endregion

        #region Text Recognition

        private void StartAnalysis ()
        {
            if (_Overlay != null)
            {
                try
                {
                    foreach (string s in RecognizedTexts)
                    {
                        if (DoInkAnalyzed (s))
                        {
                            Clear ();
                        }
                    }
                }
                catch (Exception)
                {
                }
            }
        }

        /// <summary>
        ///     This event is called when ink has been analyzed.
        /// </summary>
        /// <remarks><para>
        ///     The event will be called multiple times if the ink
        ///     can be interpreted in multiple ways.
        /// </para><para>
        ///     The target should set Accepted to true once an interpretation
        ///     is valid. This will clear the ink.
        /// </para></remarks>

        public event EventHandler<InkAnalysisEventArgs> InkAnalyzed;

        protected bool DoInkAnalyzed (string aPossibleInterpretation)
        {
            aPossibleInterpretation = aPossibleInterpretation.Trim ();
            if (String.IsNullOrEmpty (aPossibleInterpretation))
            {
                return false;
            }
            else
            {
                InkAnalysisEventArgs e = new InkAnalysisEventArgs (aPossibleInterpretation);
                OnInkAnalyzed (e);
                return e.Accepted;
            }
        }

        protected virtual void OnInkAnalyzed (InkAnalysisEventArgs e)
        {
            if (InkAnalyzed != null)
            {
                InkAnalyzed (this, e);
            }
        }

        #endregion

        #region Attached Control

        protected void UpdateAttachedControl (Control c)
        {
            if (c == null || !_InkEnabled)
            {
                return;
            }
            if (c is StandardUserControl)
            {
                DoUpdateAttachedControl ((StandardUserControl)c);
                return;
            }
            if (c is StandardControl)
            {
                DoUpdateAttachedControl ((StandardControl)c);
                return;
            }
            UpdateAttachedControl (c.Parent);
        }

        protected void UpdateAttachedControl ()
        {
            UpdateAttachedControl (_Control);
        }

        protected virtual void DoUpdateAttachedControl (StandardUserControl c)
        {
            c.AllowDragStart = false;
        }

        protected virtual void DoUpdateAttachedControl (StandardControl c)
        {
            c.AllowDragStart = false;
        }

        /// <summary>
        ///     The control on which ink recognition should be attached.
        /// </summary>

        public Control AttachedControl
        {
            get
            {
                return _Control;
            }
            set
            {
                if (_Control != value)
                {
                    if (_Overlay != null)
                    {
                        _Overlay.Dispose ();
                        _Overlay = null;
                    }
                    if (_Control != null)
                    {
                        _Control.ParentChanged -= new EventHandler (_Control_ParentChanged);
                    }

                    _Control = value;
                    if (_Control != null && _InkEnabled)
                    {
                        _Overlay = new InkOverlay (_Control);
                        _Overlay.Stroke += new InkCollectorStrokeEventHandler (_Collector_Stroke);
                        try
                        {
                            _Overlay.Enabled = true;
                        }
                        catch
                        {
                            _InkEnabled = false;
                            _Overlay.Dispose ();
                            _Overlay = null;
                        }
                        if (_InkEnabled)
                        {
                            _Control.ParentChanged += new EventHandler (_Control_ParentChanged);
                            UpdateAttachedControl ();
                        }
                    }
                }
            }

        }

        void _Control_ParentChanged (object sender, EventArgs e)
        {
            UpdateAttachedControl ();
        }

        #endregion

        /// <summary>
        ///     Clears all strokes.
        /// </summary>

        public void Clear ()
        {
            if (_Overlay != null)
            {
                _Overlay.Ink.DeleteStrokes ();
                if (_Control != null)
                {
                    _Control.Invalidate ();
                }
            }
        }

        public string BestRecognizedText
        {
            get
            {
                if (_Overlay == null)
                {
                    return "";
                }
                else
                {
                    try
                    {
                        return _Overlay.Ink.Strokes.ToString ();
                    }
                    catch (Exception e)
                    {
                        return "";
                    }
                }
            }
        }

        public IEnumerable<string> RecognizedTexts
        {
            get
            {
                if (_Overlay != null)
                {
                    string s = BestRecognizedText;
                    if (!String.IsNullOrEmpty (s))
                    {
                        yield return s;
                    }
                }
            }
        }
    }
}
